class TeamNotFoundInMatch < Exception
  def initialize(team)
    super "#{team && team.name} not found in match"
  end
end

class Match < ActiveRecord::Base
  validates_presence_of :away_team_id,:home_team_id,:sport_id,:point_spread
  after_save :calc_challenge

  def calc_challenge
    logger.info('===========================saved================')
    if played and winner_id
      # add back the point to not accept challenge
      challenges.not_accept_bet.each do |ch|
	  ch.sender.point = ch.sender.point + ch.amount 
	  ch.sender.save
          PointHistory.create(:user_id=>ch.sender.id,:action=>'return back the unaccept challenge winpoint',:point=>ch.amount,:earning => true)
      end

      challenges.placed_bet.each do |ch|
	sbet = ch.sender.bet(self)
	if sbet.team_id == winner_id
	   ch.sender.point = ch.sender.point + ch.amount * 2
	   ch.sender.save
           PointHistory.create(:user_id=>ch.sender.id,:action=>'win challenge winpoints ' + self.info,:point=>ch.amount,:earning => true)
	end
	gbet = ch.goter.bet(self)
	if gbet.team_id == winner_id
	   ch.goter.point = ch.goter.point + ch.amount*2 
	   ch.goter.save
           PointHistory.create(:user_id=>ch.goter.id,:action=>'win challenge winpoints ' + self.info,:point=>ch.amount,:earning => true)
	end
        ch.mode = "bet_2"
	ch.save
      end
    end
    logger.info('===========================saved================')
  end

  has_many :challenges 
  has_many :bets,:dependent=>:destroy do
    def away
      scoped(
        :conditions=>[
          "#{Bet.table_name}.team_id = ?",
          proxy_owner.away_team_id
        ]
      )
    end

    def home
      scoped(
        :conditions=>[
          "#{Bet.table_name}.team_id = ?",
          proxy_owner.home_team_id
        ]
      )
    end
    def home_friends(ids)
      scoped(
        :conditions=>[
          "#{Bet.table_name}.team_id = ? and user_id in (#{ids})",
          proxy_owner.home_team_id
        ]
      )
    end
    def away_friends(ids)
      scoped(
        :conditions=>[
          "#{Bet.table_name}.team_id = ? and user_id in (#{ids})",
          proxy_owner.away_team_id
        ]
      )
    end
    def friends(ids)
      scoped(
        :conditions=>[
          "user_id in (#{ids})",
          proxy_owner.away_team_id
        ]
      )
    end
  end

  has_many :players,:through=>:bets,:source=>:user
  belongs_to :sport
  belongs_to :winner,:class_name=>"Team",:foreign_key=>"winner_id"
  belongs_to :home_team,:class_name=>"Team",:foreign_key=>"home_team_id"
  belongs_to :away_team,:class_name=>"Team",:foreign_key=>"away_team_id"
  belongs_to :league

  named_scope :played,lambda { |played|
    {
      :conditions=>{:played=>played}
    }
  }


  # two teams should not same
  def validate_on_create
    errors.add_to_base "two team can't be same." if home_team_id == away_team_id
  end

  def half_value?
    p = point_spread.to_f.abs
    d = p.ceil - p
    [0.25,0.75].include?(d)
  end

  def hit_half_value?(point)
    if point_spread.to_f > 0
      half_value? && point_spread.ceil == point
    else
      half_value? && point_spread.floor == point
    end
  end
  

  # process the result , away team win . or home team win.
  # if processed , the flag will set to true
  def play(who_win,winstreak_value)
    unless played?
      raise TeamNotFoundInMatch.new(who_win) unless [nil,home_team,away_team].include?(who_win)
      self.winner = who_win
      self.winstreak_value = winstreak_value
      self.played = true
      save(false)
      # this may take long time when bets was a large number, such as 100000
      # we must process the bet. so , we can't just use sql to update it
      bets.each{ |bet| bet.play(self) }
      # now we need update the bets , set as win or loss.
      # bets.update_all ["played = ?",true] # set all bets as played
      # bets.update_all ["win = ?",true],["team_id = ?",who_win.id] # set win flag
    end
  end

  # give a centain home point and away point
  # this will calculate winner automaticialy
  #
  # total score bet
  # bet home team , means higher total score
  # bet away team , means lower total score
  #
  def play_from_point(home_point=nil,away_point=nil)
    home_point ||= self.home_point
    away_point ||= self.away_point
    self.home_point = home_point.to_f
    self.away_point = away_point.to_f
    raise "match already played" if played?
    raise "home point can't be empty" unless home_point
    raise "away point can't be empty" unless away_point
    winner,winstreak_value = (bet_type == "total" ? play_for_total : play_for_spread)

    winstreak_value = 0 if winner.nil? # fix for draw 

    play(winner,winstreak_value)
    winner # we return winner
  end

  def play_for_total
    winner = case total_point <=>  home_point + away_point
      when 0
        nil
      when 1
        away_team
      when -1
        home_team
    end
    return winner,1
  end

  def play_for_spread
    point_delta = (home_point + point_spread) - away_point
    value = 1
    if point_delta.zero?
      # draw
      winner = nil
    elsif point_delta < 0
      winner = away_team
      value = 0.5 if point_delta == -0.25
    elsif point_delta > 0
      winner = home_team
      value = 0.5 if point_delta == 0.25
    end

    return winner,value
  end

  private :play_for_total,:play_for_spread

  def home_point_spread
    point_spread.to_f
  end

  def away_point_spread
     - home_point_spread
  end


  def self.fetch_from_teams(team_name1,team_name2)
    team1 = Team.find_by_name(team_name1)
    team2 = Team.first(:conditions=>["name = ? OR name_alias = ?",team_name2,team_name2])
    if team1 && team2
      Match.find_by_away_team_id_and_home_team_id(team1.id,team2.id)
    else
      logger.error "team2 #{team_name2} mapping not exsist."
      nil
    end
  end

  # next match need to process
  def self.next_match
    Match.played(false).first(:order=>"begin_at asc ,id asc")
  end


  def self.from_hash(hash,league)
    # puts hash.inspect
    match = Match.new
    return if Match.find_by_mid(hash["id"].to_i) # already fetched
    odds = hash['asianodds'] || hash['handicapodds']
    return if odds.nil?
    bms = [odds["bm"]].flatten

    handicap = select_bm(bms)
    # raise handicap.inspect
    return if handicap.nil? # handicap not match conditions

    home_team = Team.find_by_name(hash["hometeam"]) || Team.find_or_create_by_name_and_sport_id(
      hash["hometeam"],
      league.sport_id
    )
    # puts home_team.inspect

    away_team = Team.find_by_name(hash["awayteam"]) || Team.find_or_create_by_name_and_sport_id(
      hash["awayteam"],
      league.sport_id
    )
    # puts away_team.inspect

    match.begin_at = DateTime.parse hash["timeplayed"]
    #puts "handicap: #{handicap.inspect}"
    #puts "#{handicap['hl']}:#{handicap['hh']}"
    match.point_spread = handicap["hl"] || handicap["hh"]
    #puts match.point_spread
    match.away_team = away_team
    match.home_team = home_team
    match.league = league
    match.sport  = league.sport
    match.mid = hash["id"]
    match.bet_type = "spread"
    begin
      match.save!
      #puts match.point_spread
      # match.reload
      #puts match.point_spread
      #puts "id #{match.id}"
    rescue Exception=>e
      # ...
    end
    match
  end



  def info
    "#{id} - #{home_team.name}(#{home_team.name_alias}) vs #{away_team.name}(#{away_team.name_alias}) [#{begin_at}]"
  end

  private
    # select bm from bms
    # bm id=53 , |ho-ao| = |2.12 - 1.82| = 0.3
    # bm id=54 , |ho-ao| = |2.15 - 1.77| = 0.38
    # bm id=107 , |ho-ao| = |2.09 - 1.81| = 0.28
    # bm id=107, |ho-ao| = |1.84 - 2.05| = 0.21
    # bm id=54, |ho-ao| = |1.67 - 2.10| = 0.43
    def self.select_bm(bms)
      #puts bms.inspect
      bms_hash = {}
      bms.collect do |bm|
        delta = bm["ho"].to_f - bm["ao"].to_f
        bms_hash[delta.abs] = bm
      end
      # puts bms_hash.inspect
      delta = bms_hash.keys.sort.first
      if delta <= MIN_HANDICAP
        bms_hash[delta]
      else
        nil
      end
    end

end
